package com.android.settings.audio.peq.soundeffect;

import android.os.Build;
import android.os.SystemClock;
import android.text.TextUtils;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.android.settings.R;
import com.android.settings.audio.peq.util.BuildConfig;
import com.android.settings.audio.peq.util.LogPlus;
import com.android.settings.audio.peq.util.ShareprefenceTool;
import com.android.settings.audio.peq.util.SmartPlayerApplication;
import com.android.settings.audio.peq.util.ToastTool;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Created by We on 2023/6/3
 */
public class PluginDataManager {
    private Map<String, List<PluginDataItem>> dataItemsMap;
    public static final String NONE_ID = "_none_";

    private PluginDataManager() {
        gson = new Gson();
        tempDataItem = new PluginDataItem();
        tempDataItem.id = NONE_ID;
        tempDataItem.name = "";
        dataItemsMap = new HashMap<>();
        dataItemsMap.put(KEY_EQ, getDataItemsFromLocal(KEY_EQ));
        dataItemsMap.put(KEY_PEQ, getDataItemsFromLocal(KEY_PEQ));
        dataItemsMap.put(KEY_MSEB, getDataItemsFromLocal(KEY_MSEB));
    }

    public void clearServerDataId(String type, String serverRecordId) {
        List<PluginDataItem> dataItems = getDataItemsFromLocal(type);
        if (dataItems != null) {
            PluginDataItem targetDataItem = null;
            for (PluginDataItem eqDataItem : dataItems) {
                if (serverRecordId.equals(eqDataItem.getServerDataId())) {
                    targetDataItem = eqDataItem;
                    break;
                }
            }
            if (targetDataItem != null) {
                targetDataItem.setServerDataId(null);
                saveDataItems(type, dataItems);
            }
        }
    }

    private static final class InstanceHolder {
        private static final PluginDataManager instance = new PluginDataManager();
    }

    public static PluginDataManager getInstance() {
        return InstanceHolder.instance;
    }

    private static final String DATA_ITEMS_KEY = "dataItems";

    public List<PluginDataItem> getDataItemsFromLocal(String type) {
        if (dataItemsMap.get(type) != null) {
            return dataItemsMap.get(type);
        }
        String json = ShareprefenceTool.getInstance().getStringShareprefence(DATA_ITEMS_KEY + type, SmartPlayerApplication.getInstance(), "");
        if (TextUtils.isEmpty(json)) {
            List<PluginDataItem> dataItems = new ArrayList<>();
            dataItemsMap.put(type, dataItems);
            return dataItems;
        }
        List<PluginDataItem> dataItems = gson.fromJson(json, new TypeToken<List<PluginDataItem>>() {
        }.getType());
        if (dataItems == null) {
            dataItems = new ArrayList<>();
        }
        dataItemsMap.put(type, dataItems);
        return dataItems;
    }

    private final PluginDataItem tempDataItem;

    public void addDataItem(String type, PluginDataItem dataItem) {
        List<PluginDataItem> dataItems = getDataItemsFromLocal(type);
        if (dataItems == null) {
            dataItems = new ArrayList<>();
            if (BuildConfig.DEBUG) LogPlus.d("###pluginData:" + dataItem.getPluginData());
            savePluginData(type, dataItem.pluginData);
            dataItemsMap.put(type, dataItems);
        }
        if (!dataItems.contains(dataItem)) {
            if (false) {
                ///////////todo 导入可以导入成功，但是没有去重，名字相同的不断导入。可以提醒重复名字，建议用户修改///////////
                List<PluginDataItem> localPluginDataItems = getDataItemsFromLocal(type);
                boolean isExists = false;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    isExists = localPluginDataItems.stream().anyMatch(pluginDataItem -> pluginDataItem.getName().equals(dataItem.getName()));
                } else {
                    for (PluginDataItem pluginDataItem : localPluginDataItems) {
                        if (pluginDataItem.getName().equals(dataItem.getName())) {
                            isExists = true;
                            break;
                        }
                    }
                }
                if (isExists) {
                    ToastTool.showToast(SmartPlayerApplication.getInstance(), SmartPlayerApplication.getInstance().getString(R.string.peq_plg_config_samename_exists));
                }
                ///////////todo 导入可以导入成功，但是没有去重，名字相同的不断导入。可以提醒重复名字，建议用户修改///////////
            }

            dataItems.add(dataItem);
            if (BuildConfig.DEBUG) LogPlus.d("###pluginData:" + dataItem.getPluginData());
            savePluginData(type, dataItem.pluginData);
            saveDataItems(type, dataItems);
        }
    }

    public PluginData getPluginData(String type, String id) {
        String json = ShareprefenceTool.getInstance().getStringShareprefence(PLUGIN_DATA_KEY + type + id, SmartPlayerApplication.getInstance(), "");
        if (TextUtils.isEmpty(json)) {
            // 继续从本地已保存的 dataItems 中读取, 因为在线资源列表未保存在本地
            PluginDataItem pluginDataItem = getDataItem(type, id);
            if (pluginDataItem != null) {
                return pluginDataItem.getPluginData();
            }
            return null;
        }
        try {
            return gson.fromJson(json, PluginData.class);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    private static final String PLUGIN_DATA_KEY = "PLUGIN_DATA_KEY_";

    public void savePluginData(String type, PluginData pluginData) {
        String json = gson.toJson(pluginData);
        ShareprefenceTool.getInstance().setStringSharedPreference(PLUGIN_DATA_KEY + type + pluginData.getId(), json, SmartPlayerApplication.getInstance());
    }

    public <T> T getPluginDataValue(String type, String id, TypeToken<T> typeToken) {
        PluginData pluginData = getPluginData(type, id);
        if (pluginData == null) return null;
        if (pluginData.getValue() == null) return null;
        Object data = pluginData.getValue();
        if (BuildConfig.DEBUG) LogPlus.d("###json data : " + data + " ,type:" + type + " ,id:" + id);
        try {
            return (T) gson.fromJson(gson.toJson(data), typeToken.getType());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    public boolean updateDataItem(String type, PluginDataItem dataItem) {
        if (dataItem.id == null) throw new RuntimeException("id must not be null");
        List<PluginDataItem> items = getDataItemsFromLocal(type);

        PluginDataItem targetDataItem = null;
        for (PluginDataItem item : items) {
            if (item.id.equals(dataItem.id)) {
                targetDataItem = item;
                break;
            }
        }

        if (null == targetDataItem) {
            return false;
        }

        targetDataItem.name = dataItem.name;
        targetDataItem.device = dataItem.device;
        targetDataItem.description = dataItem.description;
        targetDataItem.serverDataId = dataItem.serverDataId;
        targetDataItem.setPluginData(dataItem.pluginData);

        saveDataItems(type, items);

        savePluginData(type, dataItem.pluginData);
        return true;
    }

    private Gson gson;

    public void saveDataItems(String type, List<PluginDataItem> dataItems) {
        String json = gson.toJson(dataItems);
        if (BuildConfig.DEBUG) LogPlus.d("###json:" + json);
        ShareprefenceTool.getInstance().setStringSharedPreference(DATA_ITEMS_KEY + type, json, SmartPlayerApplication.getInstance());
    }

    public <T> T jsonToData(String json, Class clazz) throws Exception {
        try {
            return (T) gson.fromJson(json, clazz);
        } catch (Exception e) {
            throw e;
        }
    }

    public <T> T jsonToData(Object data, TypeToken<T> typeToken) {
        if (data == null) return null;
        try {
            if (!(data instanceof String)) {
                data = gson.toJson(data);
            }
            return (T) gson.fromJson((String) data, typeToken.getType());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    public String toJson(Object o) {
        return gson.toJson(o);
    }

    public boolean deleteData(String type, String id) {
        PluginDataItem eqDataItem = getDataItem(type, id);
        if (eqDataItem != null) {
            if (eqDataItem.ischecked()) {
                tempDataItem.setIschecked(true);
            }
            List<PluginDataItem> dataItems = getDataItemsFromLocal(type);
            boolean isSuccess = dataItems.remove(eqDataItem);
            if (isSuccess) saveDataItems(type, dataItems);
            return isSuccess;
        }
        return false;
    }

    public PluginDataItem getDataItem(String type, String id) {
        if (id == null) throw new RuntimeException("id must not be null");
        List<PluginDataItem> dataItems = getDataItemsFromLocal(type);
        return getDataItem(id, dataItems);
    }

    public PluginDataItem getDataItem(String id, List<PluginDataItem> eqDataItems) {
        if (id == null) throw new RuntimeException("id must not be null");
        for (PluginDataItem dataItem : eqDataItems) {
            if (id.equals(dataItem.getId())) {
                return dataItem;
            }
        }
        return null;
    }

    public boolean isExists(String type, String name) {
        if (TextUtils.isEmpty(name)) return true;
        List<PluginDataItem> localPluginDataItems = getDataItemsFromLocal(type);
        boolean isExists = false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            isExists = localPluginDataItems.stream().anyMatch(pluginDataItem -> pluginDataItem.getName().equals(name));
        } else {
            for (PluginDataItem pluginDataItem : localPluginDataItems) {
                if (pluginDataItem.getName().equals(name)) {
                    isExists = true;
                    break;
                }
            }
        }
        return isExists;
    }

    public static final String KEY_EQ = "KEY_EQ";
    public static final String KEY_PEQ = "KEY_PEQ";
    public static final String KEY_MSEB = "KEY_MSEB";


    public static class PluginDataItem {
        public String id;
        public String name;
        public boolean ischecked;

        private String device;
        private String description;
        private String serverDataId;//如果本地保存成功之后则记录服务器端配置的record id
        private PluginData pluginData;

        public PluginDataItem() {
            this.id = System.currentTimeMillis() + "";
        }

        public String getName() {
            if (name == null) {
                name = "unknown";
            }
            return name;
        }

        public void setName(String name) {
            if (name == null) {
                name = "unknown";
            }
            this.name = name;
        }

        public boolean ischecked() {
            return ischecked;
        }

        public void setIschecked(boolean ischecked) {
            this.ischecked = ischecked;
        }

        public void setServerDataId(String serverDataId) {
            this.serverDataId = serverDataId;
        }

        public String getServerDataId() {
            return serverDataId;
        }

        public String getDevice() {
            return device;
        }

        public void setDevice(String device) {
            this.device = device;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getId() {
            return id;
        }

        public void setPluginData(PluginData pluginData) {
            this.pluginData = pluginData;
        }

        public PluginData getPluginData() {
            return pluginData;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            PluginDataItem that = (PluginDataItem) o;
            return id.equals(that.id);
        }

        @Override
        public int hashCode() {
            return Objects.hash(id);
        }

        @Override
        public String toString() {
            return "PluginDataItem{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", ischecked=" + ischecked +
                    ", device='" + device + '\'' +
                    ", description='" + description + '\'' +
                    ", serverDataId='" + serverDataId + '\'' +
                    ", pluginData=" + pluginData +
                    '}';
        }
    }

    public static class PluginData implements java.io.Serializable {
        protected String id;
        protected transient/*2023.6.15 服务器端限制不能包含特殊符号,忽略*/ String name;

        protected Object value;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "PluginData{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    '}';
        }

        public Object getValue() {
            return value;
        }

        public void setValue(Object value) {
            this.value = value;
        }

        public PluginData() {
            this.id = SystemClock.uptimeMillis() + "";
        }

        public PluginData(String id, String name, Object value) {
            this.id = id;
            this.name = name;
            this.value = value;
        }
    }

    public PeqData newNonePeqData() {
        PeqData nonePeqData = new PeqData();
        nonePeqData.name = SmartPlayerApplication.getInstance().getString(R.string.peq_mseb_none);
        nonePeqData.pre_amp = 0;
        nonePeqData.bands = new ArrayList<>();
        final int[] freqs = {31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000};
        final int bandsCount = freqs.length;
        for (int freq : freqs) {
            BandItem bandItem = new BandItem();
            bandItem.filter = 0;
            bandItem.freq = freq;
            bandItem.on = 0;
            bandItem.gain = 0;
            bandItem.q = 0.2;
            nonePeqData.bands.add(bandItem);
        }
        return nonePeqData;
    }

    public static class BaseData implements java.io.Serializable {
        public String name;
        public String device;
        public String description;
    }

    public static class PeqData extends BaseData implements java.io.Serializable {
        public List<BandItem> bands;
        public double pre_amp;

        @Override
        public String toString() {
            return "PeqData{" +
                    "bands=" + bands +
                    ", name='" + name + '\'' +
                    ", pre_amp=" + pre_amp +
                    '}';
        }
    }

    public static class BandItem implements java.io.Serializable {
        public double freq;
        public double q;
        public double gain;
        public int on;
        public int filter;

        @Override
        public String toString() {
            return "BandItem{" +
                    "freq=" + freq +
                    ", q=" + q +
                    ", gain=" + gain +
                    ", on=" + on +
                    ", filter=" + filter +
                    '}';
        }

        public BandItem setFreq(double freq) {
            this.freq = freq;
            return this;
        }

        public BandItem setQ(double q) {
            this.q = q;
            return this;
        }

        public BandItem setGain(double gain) {
            this.gain = gain;
            return this;
        }

        public BandItem setOn(int on) {
            this.on = on;
            return this;
        }

        public BandItem setFilter(int filter) {
            this.filter = filter;
            return this;
        }
    }

    public EqData newNoneEqData() {
        EqData eqData = new EqData();
        eqData.name = "";
        eqData.device = "";
        eqData.description = "";
        eqData.value = new float[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
        return eqData;
    }

    public static class EqData extends BaseData implements java.io.Serializable {
        public float[] value;

        @Override
        public String toString() {
            return "EqData{" +
                    "value=" + Arrays.toString(value) +
                    ", name='" + name + '\'' +
                    ", device='" + device + '\'' +
                    ", description='" + description + '\'' +
                    '}';
        }
    }

    public static class MsebData extends BaseData implements java.io.Serializable {
        public int range;
        public List<Integer> datalist;
        public List<Integer> result;
    }

    public interface OnSaveItemCallback {
        void onSuccess(PluginDataItem dataItem);
    }
}
